package com.googlecode.jtoy.jdk6.regex;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.Test;

/**
 * java中运用正则表达式的两个重要类:Pattern与Matcher
 * Pattern类说明
 * 
 * 指定为字符串的Java正则表达式必须首先被编译为pattern类的实例。然后，可将得到的模式用于创建 Matcher
 * 对象，依照Java正则表达式，该对象可以与任意字符序列匹配。执行匹配所涉及的所有状态都驻留在匹配器中，所以多个匹配器可以共享同一模式。
 * 
 * 因此，典型的调用顺序是
 * 
 * Pattern p = Pattern.compile("a*b");
 * Matcher m = p.matcher("aaaaab");
 * boolean b = m.matches();
 * 在仅使用一次正则表达式时，可以方便地通过pattern类定义 matches 方法。此方法编译表达式并在单个
 * 调用中将输入序列与其匹配。语句
 * boolean b = Pattern.matches("a*b", "aaaaab");
 * 
 * 等效于上面的三个语句，尽管对于重复的匹配而言它效率不高，因为它不允许重用已编译的模式。
 * 此类的实例是不可变的，可供多个并发线程安全使用。Matcher 类的实例用于此目的则不安全。
 * 
 * Matcher类说明
 * 
 * 通过调用模式的 matcher 方法从模式创建匹配器。创建匹配器后，可以使用它执行三种不同的匹配操作：
 * 
 * 1 matches 方法尝试将整个输入序列与该模式匹配。
 * 
 * (注:当调用String的matches()方法时,实际上是调用Pattern的静态方法matches().也就是相当于调Matcher的matches(),所以是整个输入序列与模式匹配.)
 * 
 * 2 lookingAt 尝试将输入序列从头开始与该模式匹配。
 * 
 * 3 find 方法扫描输入序列以查找与该模式匹配的下一个子序列。
 * 
 * 此类的实例用于多个并发线程是不安全的。
 * 
 * @author <a href="mailto:sxjvip@gmail.com">shangxj</a>
 * @date Jul 2, 2011 10:27:22 PM
 */
public class PatternAndMatcherTest {

	@Test
	public void testIsMatched() {

		Pattern patt = Pattern.compile("a*b\\.?.*");
		System.out.println(patt.pattern());

		assertTrue(patt.matcher("aaab").matches());
		assertTrue(patt.matcher("b").matches());

		/**
		 * 另一种调用
		 * 等效于上面的语句，尽管对于重复的匹配而言它效率不高，因为它不允许重用已编译的模式。
		 * 但它可供多个并发线程安全使用,而上面的调用则就不是安全的.
		 */
		assertTrue(Pattern.matches("a*b", "aaab"));
	}

	@Test
	public void testSplit() {

		Pattern patt = Pattern.compile("a*b");

		// Pattern类的split方法
		String[] subStrs = patt.split("rrrrraaabccccaaaaab");

		assertEquals("[rrrrr, cccc]", Arrays.toString(subStrs));
	}

	@Test
	public void testMatches() {

		/*
		 * Matcher类
		 * 
		 * matches方法: 方法尝试将整个输入序列与该模式匹配
		 * lookingAt方法: 尝试将输入序列从头开始与该模式匹配,与 matches 方法类似，
		 * 此方法始终从区域的开头开始；与之不同的是，它不需要匹配整个区域。
		 * find方法: 方法扫描输入序列以查找与该模式匹配的下一个子序列
		 */
		/*
		 * matches方法: 方法尝试将整个输入序列与该模式匹配
		 */
		assertFalse(Pattern.compile("a+").matcher("aaaa").matches());
		assertFalse(Pattern.compile("a+").matcher("aaab").matches());
	}

	@Test
	public void testLookingAt() {

		/*
		 * Matcher类
		 * 
		 * matches方法: 方法尝试将整个输入序列与该模式匹配
		 * lookingAt方法: 尝试将输入序列从头开始与该模式匹配,与 matches 方法类似，
		 * 此方法始终从区域的开头开始；与之不同的是，它不需要匹配整个区域。
		 * find方法: 方法扫描输入序列以查找与该模式匹配的下一个子序列
		 */

		/*
		 * lookingAt方法,从开头第一个字符进行匹配,匹配成功了不再继续匹配,
		 * 从第一个字符开始,匹配失败了,也不继续匹配.不需要匹配整个序列
		 */
		assertTrue(Pattern.compile("a+").matcher("abcd").lookingAt());
		assertTrue(Pattern.compile("b+").matcher("abcd").lookingAt());
	}

	@Test
	public void testFind() {

		/*
		 * Matcher类
		 * 
		 * matches方法: 方法尝试将整个输入序列与该模式匹配
		 * lookingAt方法: 尝试将输入序列从头开始与该模式匹配,与 matches 方法类似，
		 * 此方法始终从区域的开头开始；与之不同的是，它不需要匹配整个区域。
		 * find方法: 方法扫描输入序列以查找与该模式匹配的下一个子序列
		 */
		String str3 = "aabbcccaaaaaeeeaaaaaaaaagggga";
		Pattern patt = Pattern.compile("a+");
		Matcher matcher = patt.matcher(str3);

		/*
		 * find方法:找到一个匹配的子串,还会继续找下一个子串.
		 */
		while (matcher.find()) {
			System.out.println("find方法:  " + matcher.group());
		}

	}

	@Test
	public void testGroup() {

		/*
		 * 带参数的group方法与不带参数的group方法区别
		 * 不带参数的group方法:find方法与lookingAt方法匹配出来的子序列(上面有演示)
		 * 带参数的group方法: 返回在以前匹配操作期间由给定组捕获的输入子序列。
		 */
		String str6 = "aaabbbccc";
		Pattern p5 = Pattern.compile("(a+)(b+)(c+)");
		Matcher m5 = p5.matcher(str6);
		boolean boo = m5.matches();
		if (boo) {
			int k = m5.groupCount() + 1;// 加1就是把0下标的整个字符序列加上,它也作为一组放在0下标的位置.
			if (k > 0) {
				for (int i = 0; i <= m5.groupCount(); i++) {
					System.out.println(m5.group(i));
				}
			}
		}
	}

	@Test
	public void testFindGroup() {

		String input = "aaaabbbcccccXXaabbbcccccYYYaaaaabbccc";
		Pattern pattern = Pattern.compile("(a+)(b+)(c+)");

		Matcher matcher = pattern.matcher(input);

		while (matcher.find()) {
			System.out.printf("%s\t%s\t%s\t%s\n", matcher.group(0), matcher.group(1),
					matcher.group(2), matcher.group(3));
		}

		System.out.println(matcher.replaceFirst("OOO"));
	}

	@Test
	public void testLookahead() {

		String input =
				"<H1>测试邮件。。。</H1>" //
						+ "<img src=\"http://www.google.com.hk/intl/zh-CN/images/logo_cn.png\">"
						// 请注意如果是本地图片比如使用斜杠作为目录分隔符,如下所示
						+ "<img src=\"H:/tomcat6/webapps/ROOT/tomcat.gif\"/>"
						+ "<img src='C:/tomcat6/webapps/ROOT/tomcat.gif' alt='tomcat.gif'/>"
						+ "<img src=\"/opt/tomcat6/webapps/ROOT/tomcat.gif\"/>";
		Pattern pattern =
				Pattern.compile("<img[^<]+src=[\"'](?!http://)([^\"']*)[\"'][^>]*>",
						Pattern.CASE_INSENSITIVE);

		Matcher matcher = pattern.matcher(input);

		while (matcher.find()) {
			System.out.printf("%s\n\t%s\n", matcher.group(), matcher.group(1));
		}

	}
}
